<template>
  <div class="container">
    <h1>声纹注册</h1>
    <el-steps :active="activeStep">
      <el-step title="声纹录制第1遍">
        <div slot="description">
          <div>单击“开始录音”按钮</div>
          <div>读出下面数字</div>
          <div>结束录音并上传</div>
        </div>
      </el-step>
      <el-step title="声纹录制第2遍">
        <div slot="description">
          <div>单击“开始录音”按钮</div>
          <div>读出下面数字</div>
          <div>结束录音并上传</div>
        </div>
      </el-step>
      <el-step title="声纹录制第3遍">
        <div slot="description">
          <div>单击“开始录音”按钮</div>
          <div>读出下面数字</div>
          <div>结束录音并上传</div>
        </div>
      </el-step>
      <el-step title="声纹录制第4遍">
        <div slot="description">
          <div>单击“开始录音”按钮</div>
          <div>读出下面数字</div>
          <div>结束录音并上传</div>
        </div>
      </el-step>
      <el-step title="声纹录制第5遍">
        <div slot="description">
          <div>单击“开始录音”按钮</div>
          <div>读出下面数字</div>
          <div>结束录音并上传</div>
        </div>
      </el-step>
    </el-steps>
    <div class="intro">
      <p>1、请务必确保当前为本人账号</p>
      <p>2、确保麦克风可以使用，与麦克风距离保持在10cm左右</p>
      <p>3、开始后请使用普通话匀速、自然地读出下面数字</p>
    </div>
    <div class="content">
      <div class="number">{{randomText.substring(0,4)}} {{randomText.substring(4,8)}}</div>
      <div class="voice">
        <canvas ref="voiceCanvas"></canvas>
        <div class="voice-data">
          <p>录音时长(秒)：{{voiceParams.duration && voiceParams.duration.toFixed(5) || 0}}</p>
          <p>录音大小(字节)：{{voiceParams.fileSize || 0}}</p>
        </div>
      </div>
    </div>
    <div class="btn">
      <el-button type="primary" @click="startRecord()">开始录音</el-button>
      <el-button @click="stopRecord()">结束录音</el-button>
      <el-button @click="uploadRecord()" :loading="uploadBtnLoading">上传录音</el-button>
      <!-- <el-button @click="playRecord()">回放录音</el-button> -->
    </div>
    <!-- <input type="file" accept="audio/*;capture=microphone" ref="uploadFile"/> -->
  </div>
</template>

<script>
import Recorder from 'js-audio-recorder'
import qs from 'qs'

// 调用流程：
// 1.插入一个node节点(type=rd)
// 2.调用随机数字串接口
// 3.按照随机数字串录音
// 4.调用上传注册语音接口（node为刚刚插入的，type为rd，skpid为这个人的标识，你们来决定，text填本次录音的随机数字串）
// 一次注册对应一个spkid，node相当于一个库，你注册的人都是注册在这个库下面，和每个人对应的是spkid
export default {
  name: 'loginVoiceRegist',
  data () {
    return {
      activeStep: 1,
      recorder: null,
      type: null,
      voiceCanvas: null, // 显示波形的canvas
      ctx: null,
      drawRecordId: null,
      voiceParams: {},
      uploadBtnLoading: false,
      randomText: '',
      spkid: window.sessionStorage.getItem('spkid')
    }
  },
  mounted () {
    const voiceCanvas = this.$refs.voiceCanvas
    this.voiceCanvas = voiceCanvas
    this.ctx = voiceCanvas.getContext('2d')
    // canvas背景初始化
    this.initCanvasBg()
    this.queryRandomData()
  },
  methods: {
    startRecord () {
      if (!this.recorder) {
        this.recorder = new Recorder({
          // 以下是默认配置
          sampleBits: 16,
          sampleRate: 16000, // 浏览器默认的输入采样率,
          numChannels: 1
        })
      }
      this.recorder.start().then(() => {
        // 实时获取以下数据
        this.recorder.onprogress = (params) => {
          // console.log('录音时长(秒)', params.duration)
          // console.log('录音大小(字节)', params.fileSize)
          // console.log('录音音量百分比(%)', params.vol)
          this.voiceParams = params
        }
        // 开始绘制canvas
        this.type = 'record'
        this.drawRecord()
      }, (error) => {
        // 出错了
        console.log(`${error.name} : ${error.message}`)
      })
    },
    stopRecord () {
      this.recorder && this.recorder.stop()
      this.drawRecordId && cancelAnimationFrame(this.drawRecordId)
      this.drawRecordId = null
    },
    // canvas波形绘制函数
    drawRecord () {
      // 用requestAnimationFrame稳定60fps绘制
      this.drawRecordId = requestAnimationFrame(this.drawRecord)

      // 实时获取音频大小数据
      var dataArray = this.type === 'record' ? this.recorder.getRecordAnalyseData() : this.recorder.getPlayAnalyseData()
      var bufferLength = dataArray.length

      // 填充背景色
      this.ctx.fillStyle = 'rgb(200, 200, 200)'
      this.ctx.fillRect(0, 0, this.voiceCanvas.width, this.voiceCanvas.height)

      // 设定波形绘制颜色
      this.ctx.lineWidth = 2
      this.ctx.strokeStyle = 'rgb(0, 0, 0)'

      this.ctx.beginPath()

      var sliceWidth = this.voiceCanvas.width * 1.0 / bufferLength // 一个点占多少位置，共有bufferLength个点要绘制
      var x = 0 // 绘制点的x轴位置

      for (var i = 0; i < bufferLength; i++) {
        var v = dataArray[i] / 128.0
        var y = v * this.voiceCanvas.height / 2

        if (i === 0) {
          // 第一个点
          this.ctx.moveTo(x, y)
        } else {
          // 剩余的点
          this.ctx.lineTo(x, y)
        }
        // 依次平移，绘制所有点
        x += sliceWidth
      }
      this.ctx.lineTo(this.voiceCanvas.width, this.voiceCanvas.height / 2)
      this.ctx.stroke()
    },
    // canvas背景初始化
    initCanvasBg () {
      this.ctx.fillStyle = 'rgb(200, 200, 200)'
      this.ctx.fillRect(0, 0, this.voiceCanvas.width, this.voiceCanvas.height)
    },
    playRecord () {
      this.recorder.play()

      // 开始绘制canvas
      this.type = 'play'
      this.drawRecord()
    },
    // 请求随机动态口令
    queryRandomData () {
      this.axios.post('/kst/text').then((response) => {
        this.randomText = response.data.text
      })
    },
    // 上传一条注册语音
    uploadRecord () {
      this.uploadBtnLoading = true

      let formData = new FormData()
      formData.append('type', 'rd')
      formData.append('node', 'node1')
      formData.append('spkid', this.spkid)
      formData.append('wavtype', 'wav')
      formData.append('channel', '0')
      formData.append('step', this.activeStep) // 注册语音需多条语音上传，step表示上传的第step条语音，step >=1
      // formData.append('asrdetect', true) // 非必须，是否开启语音识别
      // formData.append('text', '123456') // 非必须，语音文本内容（用于文本相关或随机动态口令语音文本内容识别的结果判断）
      const _blob = this.recorder.getWAVBlob()
      let file = new window.File([_blob], 'demo')
      formData.append('file', file)
      console.log(file)
      this.axios({
        method: 'POST',
        headers: { 'content-type': 'multipart/form-data' },
        url: '/kst/upload',
        data: formData
      }).then((response) => {
        if (response.data.code === 0) {
          this.uploadBtnLoading = false
          this.$notify({
            title: '成功',
            message: `录音第${this.activeStep}遍上传成功`,
            type: 'success'
          })
          if (this.activeStep < 5) {
            this.activeStep = this.activeStep + 1
            this.queryRandomData()
          } else {
            // 录制5次后，执行注册
            this.registRecord()
          }
        } else {
          this.$notify.error({
            title: '失败',
            message: response.data.msg
          })
        }
      })
    },
    // 注册声纹
    registRecord () {
      this.axios({
        method: 'POST',
        headers: { 'content-type': 'application/x-www-form-urlencoded' },
        url: '/kst/enroll',
        data: qs.stringify({
          type: 'rd',
          node: 'node1',
          spkid: this.spkid
        })
      }).then((response) => {
        if (response.data.code === 0) {
          this.$notify({
            title: '成功',
            message: '声纹注册成功',
            type: 'success'
          })
        } else {
          this.$notify.error({
            title: '失败',
            message: response.data.msg
          })
        }
      })
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
.container {
  width: 1000px;
  padding: 20px 50px;
}
.intro {
  font-size: 14px;
  padding: 20px 0;
}
.intro p {
  margin: 0;
}
.number {
  font-size: 30px;
}
.voice-data {
  font-size: 14px;
}
.voice-data p {
  margin: 0;
}
.btn {
  margin-top: 20px;
}
</style>
